﻿using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Kenly.DBFramework.Reflection;
using Kenly.DBFramework;
using Kenly.DBFramework.Gateway;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    /// The gateway to a database table.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public abstract class TableGateway<T> : ViewGateway<T>, IEntityValidator,ITableGateway where T : TableGateway<T>, new()
    {
        private List<string> _changedPropertyNames = new List<string>();

        #region Properties

        /// <summary>
        /// 
        /// </summary>
        protected ITableCommandExecutor TableCommandExecutor
        {
            get;
            private set;
        }

        /// <summary>
        /// 
        /// </summary>
        private ICommandReflector CommandReflector
        {
            get;
            set;
        }

        /// <summary>
        /// The list of changed property names.
        /// </summary>
        public List<string> ChangedPropertyNames
        {
            get
            {
                return _changedPropertyNames;
            }
        }

        /// <summary>
        /// Indicates if any property changed.
        /// </summary>
        public bool Changed
        {
            get
            {
                return _changedPropertyNames.Count > 0;
            }
        }

        #endregion


        /// <summary>
        /// 
        /// </summary>
        protected TableGateway()
        {
            //Creates CommandReflector and CommandExecutor for the entity.
            this.CommandReflector = CreateCommandReflector();
            this.TableCommandExecutor = CreateCommandExecutor();
        }


        #region Instance Methods


        /// <summary>
        /// Creates CommandExecutor for the entity.
        /// </summary>
        /// <returns></returns>
        private static ITableCommandExecutor CreateCommandExecutor()
        {
            var currentConfig = Config;

            IDatabase database = DatabaseFactory.CreateInstance(currentConfig.DatabaseType);
            database.ConnectionString = currentConfig.ConnectionString;
            database.Command.CommandTimeout = currentConfig.CommandTimeout;
            return database.CreateTableCommandExecutor();
        }

        /// <summary>
        /// Creates CommandReflector for the entity.
        /// </summary>
        /// <returns></returns>
        private static ICommandReflector CreateCommandReflector()
        {
            return CommandReflectorFactory.CreateInstance(Config.DatabaseType);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="config"></param>
        public override void RefreshConfig(GatewayConfig config)
        {
            if (config == null)
            {
                return;
            }

            ConfigManager.Configs[typeof(T)] = config;
            this.TableCommandExecutor = CreateCommandExecutor();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        protected void OnPropertyChanged(string propertyName)
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                return;
            }

            if (!_changedPropertyNames.Contains(propertyName))
            {
                _changedPropertyNames.Add(propertyName);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyMethod"></param>
        protected void OnPropertyChanged(MethodBase propertyMethod)
        {
            string methodPrefix = "set_";
            if (!propertyMethod.Name.StartsWith(methodPrefix))
            {
                return;
            }

            string propertyName = propertyMethod.Name.Remove(0, methodPrefix.Length);
            OnPropertyChanged(propertyName);
        }

        internal void ClearChangedPropertyNames()
        {
            _changedPropertyNames.Clear();
        }

        #region IEntityValidator 成员

        /// <summary>
        /// 
        /// </summary>
        public virtual bool IsValid
        {
            get
            {
                List<RuleViolation> violations;
                this.Validate(out violations);

                return (violations == null) ||
                    (violations.Count == 0);
            }
        }

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <returns></returns>
        //public IEnumerable<RuleViolation> Validate()
        //{
        //    var violations = new List<RuleViolation>();

        //    this.Validate(out violations);
        //    if (violations == null)
        //    {
        //        violations = new List<RuleViolation>();
        //    }

        //    return violations;
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="violations"></param>
        /// <returns></returns>
        public virtual bool Validate(out List<RuleViolation> violations)
        {
            violations = new List<RuleViolation>();
            return violations.Count == 0;
        }



        #endregion

        #region  Transaction


        /// <summary>
        /// Begins a database transaction with 60s active time.
        /// </summary>
        /// <returns></returns>
        public IDbTransaction BeginTransaction()
        {
            return this.TableCommandExecutor.BeginTransaction();
        }

        /// <summary>
        /// Begins a database transaction with specified active time.
        /// </summary>
        /// <param name="activeTime"></param>
        /// <returns></returns>
        public IDbTransaction BeginTransaction(double activeTime)
        {
            return this.TableCommandExecutor.BeginTransaction(activeTime);
        }

        /// <summary>
        /// Begins a database transaction with specified IsolationLevel value.
        /// </summary>
        /// <param name="isolationLevel"></param>
        /// <returns></returns>
        public IDbTransaction BeginTransaction(IsolationLevel isolationLevel)
        {
            return this.TableCommandExecutor.BeginTransaction(isolationLevel);
        }

        /// <summary>
        /// Begins a database transaction with specified IsolationLevel value and active time.
        /// </summary>
        /// <param name="isolationLevel"></param>
        /// <param name="activeTime"></param>
        /// <returns></returns>
        public IDbTransaction BeginTransaction(IsolationLevel isolationLevel, double activeTime)
        {
            return this.TableCommandExecutor.BeginTransaction(isolationLevel, activeTime);
        }

        /// <summary>
        /// Commits the database transaction.
        /// </summary>
        /// <returns></returns>
        public bool Commit()
        {
            return this.TableCommandExecutor.Commit();
        }

        /// <summary>
        /// Rolls the database transaction.
        /// </summary>
        /// <returns></returns>
        public bool Rollback()
        {
            return this.TableCommandExecutor.Rollback();
        }


        #endregion  //Transaction


        /// <summary>
        /// 
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        internal protected override void RetrieveBySql(string sql)
        {
            base.RetrieveBySql(sql);
            this.Entity.ClearChangedPropertyNames();
        }


        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <returns></returns>
        public int AddNew()
        {
            return this.AddNew(false);
        }

        /// <summary>
        ///Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <returns>The number of rows affected.</returns>
        public int AddNew(bool returnIdentity)
        {
            int affectedRowCount = CreateGatewayUtility().AddNew(this.TableCommandExecutor, this.CommandReflector, this.Entity, returnIdentity);
            return affectedRowCount;
        }

        ///<summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <returns>The number of rows affected.</returns>
        public int UpdateById()
        {
            return UpdateByIdPK(this.TableCommandExecutor, this.CommandReflector, this.Entity, true);
        }


        /// <summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <returns></returns>
        public int UpdateByPK()
        {
            return UpdateByIdPK(this.TableCommandExecutor, this.CommandReflector, this.Entity, false);
        }

        /// <summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="tableCommandExecutor"></param>
        /// <param name="commandReflector"></param>
        /// <param name="entity"></param>
        /// <param name="byId"></param>
        /// <returns></returns>
        private static int UpdateByIdPK(ITableCommandExecutor tableCommandExecutor, ICommandReflector commandReflector,
            T entity, bool byId)
        {
            if (entity == null)
            {
                return 0;
            }

            List<string> propertyNames = entity.ChangedPropertyNames;
            if (propertyNames == null || propertyNames.Count <= 0)
            {
                return 0;
            }
            List<IDataParameter> paras = ParameterReflection.UpdateByIdPK(Config.DatabaseType, entity, byId, propertyNames);
            if (paras == null || paras.Count <= 0)
            {
                return 0;
            }
            string sql = byId ? commandReflector.UpdateById(entity, propertyNames) : commandReflector.UpdateByPK(entity, propertyNames);
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(sql, false, paras);
            if (affectedRowCount > 0)
            {
                entity.ClearChangedPropertyNames();
            }
            return affectedRowCount;
        }


        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <returns></returns>
        public int DeleteById()
        {
            string sql = this.CommandReflector.DeleteById(this.Entity);
            int affectedRowCount = DeleteBySql(this.TableCommandExecutor, sql);

            return affectedRowCount;
        }

        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <returns></returns>
        public int DeleteByPK()
        {
            string sql = this.CommandReflector.DeleteByPK(this.Entity);
            int affectedRowCount = DeleteBySql(this.TableCommandExecutor, sql);

            return affectedRowCount;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="tableCommandExecutor"></param>
        /// <param name="sql"></param>
        /// <returns></returns>
        private static int DeleteBySql(ITableCommandExecutor tableCommandExecutor, string sql)
        {
            if (string.IsNullOrEmpty(sql))
            {
                return 0;
            }
            int affectedRowCount = tableCommandExecutor.ExecuteNonQuery(sql, false);
            return affectedRowCount;
        }


        #endregion //Instance Methods




        #region Class Methods

        private static ITableGatewayUtility<T> CreateGatewayUtility()
        {
            return TableGatewayUtilityFactory<T>.CreateInstance(Config.DatabaseType, Config.ConnectionString);
        }


        #region AddNew


        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int AddNew(T entity)
        {
            return CreateGatewayUtility().AddNew(entity);
        }

        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int AddNew(T entity, out Transaction transaction)
        {
            return CreateGatewayUtility().AddNew(entity, out transaction);
        }

        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="returnIdentity"></param>
        /// <returns></returns>
        public static int AddNew(T entity, bool returnIdentity)
        {
            return CreateGatewayUtility().AddNew(entity, returnIdentity);
        }

        /// <summary>
        /// Adds an entity to table,returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="returnIdentity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int AddNew(T entity, bool returnIdentity, out Transaction transaction)
        {
            return CreateGatewayUtility().AddNew(entity, returnIdentity, out transaction);
        }

        /// <summary>
        /// Adds an array of entities into a table and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static int AddNew(IList<T> entities)
        {
            return CreateGatewayUtility().AddNew(entities);
        }

        /// <summary>
        /// Adds an array of entities into a table and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int AddNew(IList<T> entities, out Transaction transaction)
        {
            return CreateGatewayUtility().AddNew(entities, out transaction);
        }

        /// <summary>
        /// Adds an array of entities into a table and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="returnIdentity"></param>
        /// <returns></returns>
        public static int AddNew(IList<T> entities, bool returnIdentity)
        {
            return CreateGatewayUtility().AddNew(entities, returnIdentity);
        }

        /// <summary>
        /// Adds an array of entities into a table and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="returnIdentity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int AddNew(IList<T> entities, bool returnIdentity, out Transaction transaction)
        {
            return CreateGatewayUtility().AddNew(entities, returnIdentity, out transaction);
        }

        #endregion


        #region UpdateByCondition

        /// <summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        protected static int Update(T entity, string condition)
        {
            return CreateGatewayUtility().Update(entity, condition);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="condition"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        protected static int Update(T entity, string condition, out Transaction transaction)
        {
            return CreateGatewayUtility().Update(entity, condition, out transaction);
        }

        #endregion //UpdateByCondition

        #region UpdateBy Id PK


        /// <summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int UpdateById(T entity)
        {
            return CreateGatewayUtility().UpdateById(entity);
        }

        /// <summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int UpdateById(T entity, out Transaction transaction)
        {
            return CreateGatewayUtility().UpdateById(entity, out transaction);
        }



        /// <summary>
        /// Updates an array of entities to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static int UpdateById(IList<T> entities)
        {
            return CreateGatewayUtility().UpdateById(entities);
        }

        /// <summary>
        /// Updates an array of entities to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int UpdateById(IList<T> entities, out Transaction transaction)
        {
            return CreateGatewayUtility().UpdateById(entities, out transaction);
        }

        /// <summary>
        /// Updates the entity to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int UpdateByPK(T entity)
        {
            return CreateGatewayUtility().UpdateByPK(entity);
        }

        /// <summary>
        /// Updates an array of entities to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int UpdateByPK(T entity, out Transaction transaction)
        {
            return CreateGatewayUtility().UpdateByPK(entity, out transaction);
        }

        /// <summary>
        /// Updates an array of entities to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static int UpdateByPK(IList<T> entities)
        {
            return CreateGatewayUtility().UpdateByPK(entities);
        }

        /// <summary>
        /// Updates an array of entities to table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int UpdateByPK(IList<T> entities, out Transaction transaction)
        {
            return CreateGatewayUtility().UpdateByPK(entities, out transaction);
        }

        #endregion


        #region Delete

        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int DeleteById(T entity)
        {
            return CreateGatewayUtility().DeleteById(entity);
        }

        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int DeleteById(T entity, out Transaction transaction)
        {
            return CreateGatewayUtility().DeleteById(entity, out transaction);
        }

        /// <summary>
        /// Deletes an array of entities from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static int DeleteById(IList<T> entities)
        {
            return CreateGatewayUtility().DeleteById(entities);
        }

        /// <summary>
        /// Deletes an array of entities from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int DeleteById(IList<T> entities, out Transaction transaction)
        {
            return CreateGatewayUtility().DeleteById(entities, out transaction);
        }


        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static int DeleteByPK(T entity)
        {
            return CreateGatewayUtility().DeleteByPK(entity);
        }

        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int DeleteByPK(T entity, out Transaction transaction)
        {
            return CreateGatewayUtility().DeleteByPK(entity, out transaction);
        }

        /// <summary>
        /// Deletes an array of entities from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static int DeleteByPK(IList<T> entities)
        {
            return CreateGatewayUtility().DeleteByPK(entities);
        }

        /// <summary>
        /// Deletes an array of entities from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        public static int DeleteByPK(IList<T> entities, out Transaction transaction)
        {
            return CreateGatewayUtility().DeleteByPK(entities, out transaction);
        }


        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        protected static int Delete(string condition)
        {
            return CreateGatewayUtility().Delete(condition);
        }

        /// <summary>
        /// Deletes the entity from table,and returns the number of rows affected.
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="transaction"></param>
        /// <returns></returns>
        protected static int Delete(string condition, out Transaction transaction)
        {
            return CreateGatewayUtility().Delete(condition, out transaction);
        }



        #endregion //Delete


        #endregion


    }


}